home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / HippoDraw / HippoDrawSrc1.1 / Hippo.subproj / HDraw.m < prev    next >
Encoding:
Text File  |  1992-04-25  |  23.3 KB  |  971 lines

  1. /* HDraw    By Paul Kunz    June 1991
  2.  * derived from DrawApp in Draw, a NeXT, inc. example application
  3.  * Global object for application incorporating HippoDraw.
  4.  *
  5.  * Copyright (C)  1991  The Board of Trustees of
  6.  * The Leland Stanford Junior University.  All Rights Reserved.
  7.  */
  8.  
  9. #import "HDraw.h"
  10.  
  11. const char HDraw_h_rcsid[] = HDRAW_H_ID;
  12. const char HDraw_m_rcsid[] = "$Id: HDraw.m,v 1.32 1992/04/07 17:49:12 pfkeb Rel $";
  13.  
  14. #import "DrawDocument.h"
  15. #import "HGraphicView.h"
  16. #import "DrawPageLayout.h"
  17. #import "Inspector.h"
  18. #import "InspectAxes.h"
  19. #import "InspectCut.h"
  20. #import "InspectData.h"
  21. #import "InspectPlot.h"
  22. #import "InspectStat.h"
  23. #import "InspectTuple.h"
  24. #import "NewInspector.h"
  25. #import "Plot.h"
  26. #import "draw.h"
  27.  
  28. #import <appkit/Application.h>
  29. #import <appkit/Cursor.h>
  30. #import <appkit/Listener.h>
  31. #import <appkit/Matrix.h>
  32. #import <appkit/Menu.h>
  33. #import <appkit/MenuCell.h>
  34. #import <appkit/NXColorPanel.h>
  35. #import <appkit/OpenPanel.h>
  36. #import <appkit/Pasteboard.h>
  37. #import <appkit/Text.h>
  38. #import <appkit/defaults.h>
  39. #import <appkit/nextstd.h>
  40. #import <objc/hashtable.h>    /* for NXCopyStringBuffer() */
  41. #import <objc/List.h>
  42. #import <sys/param.h>
  43. #import <ldsyms.h>
  44. #import <sys/loader.h>
  45. #import <libc.h>
  46. #import <string.h>
  47. #import <mach.h>
  48.  
  49. const int DrawVersion = 18;    /* minor version of the program */
  50.  
  51. @implementation HDraw : Object
  52. /*
  53.  * This class is used primarily to handle the opening of new documents
  54.  * and other application-wide activity (such as responding to messages from
  55.  * the tool palette).  It listens for requests from the Workspace Manager
  56.  * to open a draw-format file as well as target/action messages from the
  57.  * New and Open... menu items.  It also keeps the menus in sync by
  58.  * fielding the menu items' updateActions.
  59.  */
  60.  
  61. /* Private C functions used to implement methods in this class. */
  62.  
  63. static void initMenu(id menu)
  64. /*
  65.  * Sets the updateAction for every menu item which sends to the
  66.  * First Responder (i.e. their target is nil).  When autoupdate is on,
  67.  * every event will be followed by an update of each of the menu items
  68.  * which is visible.  This keep all unavailable menu items dimmed out
  69.  * so that the user knows what options are available at any given time.
  70.  * Returns the activate menu if is found in this menu.
  71.  */ 
  72. {
  73.     int count;
  74.     id matrix, cell;
  75.     id matrixTarget, cellTarget;
  76.  
  77.     matrix = [menu itemList];
  78.     matrixTarget = [matrix target];
  79.  
  80.     count = [matrix cellCount];
  81.     while (count--) {
  82.     cell = [matrix cellAt:count :0];
  83.     cellTarget = [cell target];
  84.     if (!matrixTarget && !cellTarget) {
  85.         [cell setUpdateAction:@selector(menuItemUpdate:) forMenu:menu];
  86.     } else if ([cell hasSubmenu]) {
  87.         initMenu(cellTarget);
  88.     }
  89.     }
  90. }
  91.  
  92. static id documentInWindow(id window)
  93. /*
  94.  * Checks to see if the passed window's delegate is a DrawDocument.
  95.  * If it is, it returns that document, otherwise it returns nil.
  96.  */
  97. {
  98.     id document = [window delegate];
  99.     return [document isKindOf:[DrawDocument class]] ? document : nil;
  100. }
  101.  
  102. static id findDocument(const char *name)
  103. /*
  104.  * Searches the window list looking for a DrawDocument with the specified name.
  105.  * Returns the window containing the document if found.
  106.  * If name == NULL then the first document found is returned.
  107.  */
  108. {
  109.     int count;
  110.     id document, window, windowList;
  111.  
  112.     windowList = [NXApp windowList];
  113.     count = [windowList count];
  114.     while (count--) {
  115.     window = [windowList objectAt:count];
  116.     document = documentInWindow(window);
  117.     if (document && (!name || !strcmp([document filename], name))) {
  118.         return window;
  119.     }
  120.     }
  121.  
  122.     return nil;
  123. }
  124.  
  125. static id openFile(const char *directory, const char *name, BOOL display)
  126. /*
  127.  * Opens a file with the given name in the specified directory.
  128.  * If we already have that file open, it is ordered front.
  129.  * Returns the document if successful, nil otherwise.
  130.  */
  131. {
  132.     id window;
  133.     char buffer[MAXPATHLEN+1], path[MAXPATHLEN+1];
  134.  
  135.     if (name && *name) {
  136.     if (!directory) {
  137.         directory = ".";
  138.     } else if (*directory != '/') {
  139.         strcpy(buffer, "./");
  140.         strcat(buffer, directory);
  141.         directory = buffer;
  142.     }
  143.     if (!chdir(directory) && getwd(path)) {
  144.         strcpy(path, directory);    /* to avoid /private/Net/... */
  145.         strcat(path, "/");
  146.         strcat(path, name);
  147.         window = findDocument(path);
  148.         if (window) {
  149.         if (display) [window makeKeyAndOrderFront:window];
  150.         return [window delegate];
  151.         } else {
  152.         return [DrawDocument newFromFile:path];
  153.         }
  154.     } else {
  155.         NXRunAlertPanel("Open", "Invalid path: %s", "OK", NULL, NULL, directory);
  156.     }
  157.     }
  158.  
  159.     return nil;
  160. }
  161.  
  162. /* Public methods */
  163.  
  164. + initialize
  165. /*
  166.  * Initializes the defaults.
  167.  */
  168. {
  169.     const NXDefaultsVector DrawDefaults = {
  170.     { "KnobWidth", "5" },
  171.     { "KnobHeight", "5" },
  172.     { "KeyMotionDelta", "1"},
  173.     { "Quit", NULL },
  174.     { NULL, NULL }
  175.     };
  176.  
  177.     NXRegisterDefaults("Draw", DrawDefaults);
  178.  
  179.     return self;
  180. }
  181. + allocFromZone:(NXZone *)aZone
  182. {
  183.     NXZone    *zone;
  184.     
  185.     zone = NXCreateZone(vm_page_size, vm_page_size, YES);
  186.     NXNameZone(zone, "HippoDraw");
  187.     self = [super allocFromZone:zone];
  188.     return self;
  189. }
  190.  
  191. + alloc
  192. {
  193.     return [self allocFromZone:NULL];
  194. }
  195.  
  196. - init
  197.  /*
  198.   * PSInit() defines all the PostScript defs we use (see draw.psw).
  199.   * setAutoupdate:YES means that updateWindows will be called after
  200.   * every event is processed (this is how we keep our inspector and
  201.   * our menu items up to date).
  202.   */
  203. {
  204.     [NXApp loadNibSection:"HDraw.nib" owner:self
  205.            withNames:YES fromZone:[self zone]];
  206.     PSInit();
  207.     [NXApp setAutoupdate:YES];
  208.     
  209.     theNewInspector = [[NewInspector allocFromZone:[self zone]] init ]; 
  210.     inspectTuple = [[InspectTuple allocFromZone:[self zone]] initInspFor:self];
  211.     inspectData =  [[InspectData allocFromZone:[self zone]] initInspFor:self];
  212.     inspectPlot =  [[InspectPlot allocFromZone:[self zone]] initInspFor:self];
  213.     inspectAxes =  [[InspectAxes allocFromZone:[self zone]] initInspFor:self];
  214.     inspectStat =  [[InspectStat allocFromZone:[self zone]] initInspFor:self];
  215.     inspectCut =   [[InspectCut allocFromZone:[self zone]]  initInspFor:self];
  216.     return self;
  217. }
  218. - infoPanel:sender
  219. {
  220.     if( ! infoPanel ){
  221.         [NXApp loadNibSection:"Info.nib" owner:self
  222.                withNames:NO fromZone:[self zone]];
  223.     }
  224.     [infoPanel makeKeyAndOrderFront:sender];
  225.     return self;    
  226. }
  227. - legalPanel:sender
  228. {
  229.     if( ! legalPanel ){
  230.         [NXApp loadNibSection:"Legal.nib" owner:self
  231.                withNames:NO fromZone:[self zone]];
  232.     }
  233.     [legalPanel makeKeyAndOrderFront:sender];
  234.     return self;    
  235. }
  236. /* General application status and information querying/modifying methods. */
  237.  
  238. - currentGraphic
  239. {
  240.     return currentGraphic;
  241. }
  242.  
  243. - currentDocument
  244. {
  245.     return documentInWindow([NXApp mainWindow]);
  246. }
  247.  
  248. - (const char *)currentDirectory
  249. {
  250.     id openpanel = [OpenPanel new];
  251.  
  252.     const char *retval = [[self currentDocument] directory];
  253.     if (!retval || !*retval) {
  254.         [openpanel setDirectory:NXHomeDirectory()];
  255.         retval = [openpanel directory];
  256.     }
  257.     return retval;
  258. }
  259.  
  260. /* Application-wide shared panels */
  261.  
  262. - saveToPanel
  263. /*
  264.  * Returns a SavePanel with the accessory view which allows the user to
  265.  * pick which type of file she wants to save.
  266.  */
  267. {
  268.     id savepanel = [SavePanel new];
  269.     [savepanel setAccessoryView:savePanelAccessory];
  270.     [spamatrix selectCellAt:0 :0];
  271.     [savepanel setRequiredFileType:"hdraw"];
  272.     return savepanel;
  273. }
  274.  
  275. - saveAsPanel
  276. /*
  277.  * Returns a regular SavePanel with "hdraw" as the required file type.
  278.  */
  279. {
  280.     id savepanel = [SavePanel new];
  281.     [savepanel setAccessoryView:nil];
  282.     [savepanel setRequiredFileType:"hdraw"];
  283.     return savepanel;
  284. }
  285.  
  286. - gridInspector
  287. /*
  288.  * Returns the application-wide inspector for a document's grid.
  289.  * Note that if we haven't yet loaded the GridView panel, we do it.
  290.  * The instance variable gridInspector is set in setGridInspector:
  291.  * since it is set as an outlet of the owner (self, i.e. DrawApp).
  292.  */
  293. {
  294.     if (!gridInspector) {
  295.     NXZone *zone = NXCreateChildZone([self zone], vm_page_size, vm_page_size, YES);
  296.     NXNameZone(zone, "GridView");
  297.     [NXApp loadNibSection:"GridView.nib" owner:self withNames:NO fromZone:zone];
  298.     NXMergeZone(zone);
  299.     }
  300.     return gridInspector;
  301. }
  302.  
  303. - inspectorPanel
  304. /*
  305.  * Returns the application-wide inspector for Graphics.
  306.  */
  307. {
  308.     return inspectorPanel;
  309. }
  310.  
  311. - newInspector
  312. {
  313.     return theNewInspector;
  314. }
  315. - pageLayout
  316. /*
  317.  * Returns the application-wide DrawPageLayout panel.
  318.  */
  319. {
  320.     static id dpl = nil;
  321.  
  322.     if (!dpl) {
  323.     dpl = [DrawPageLayout new];
  324.     [dpl setAccessoryView:plpAccessory];
  325.     [dpl setSideForm:[plpAccessory findViewWithTag:1]];
  326.     [dpl setTopBotForm:[plpAccessory findViewWithTag:2]];
  327.     }
  328.  
  329.     return dpl;
  330. }
  331.  
  332. - orderFrontInspectorPanel:sender
  333. /*
  334.  * Creates the inspector panel if it doesn't exist, then orders it front.
  335.  */
  336. {
  337.     id accessory;
  338.     NXZone *zone;
  339.     NXRect ipFrame;
  340.  
  341.     if (!inspectorPanel) {
  342.     inspectorPanel = [NXColorPanel newColorMask:NX_ALLMODESMASK];
  343.     zone = [inspectorPanel zone];
  344.     accessory = [NXApp loadNibSection:"InspectorPanel.nib" owner:self withNames:NO fromZone:zone];
  345.     [inspectorPanel setDelegate:[accessory delegate]];
  346.     [inspectorPanel setAccessoryView:[[[[accessory contentView] subviews] objectAt:0] removeFromSuperview]];
  347.     [inspectorPanel setTitle:"Inspector"];
  348.     [inspectorPanel setMode:NX_GRAYMODE];
  349.     [inspectorPanel setTarget:[accessory delegate]];
  350.     [inspectorPanel setShowAlpha:NO];
  351.     [accessory getFrame:&ipFrame];
  352.     [inspectorPanel moveTo:ipFrame.origin.x :ipFrame.origin.y];
  353.     [accessory free];
  354.     [[inspectorPanel delegate] preset];
  355.     }
  356.     [inspectorPanel orderFront:self];
  357.  
  358.     return self;
  359. }
  360. - orderFrontTupleInsp:sender
  361. {
  362.  
  363.     theNewInspector = [ self newInspector];
  364.     [theNewInspector orderFrontPanel:self];
  365.     return self;
  366. }
  367. - inspectTuple
  368. {
  369.     return inspectTuple;
  370. }
  371. - addTuple:(ntuple) nt
  372. {
  373.     return [inspectTuple addTuple:nt];
  374. }
  375. - inspectPlot
  376. {
  377.     return inspectPlot;
  378. }
  379. - inspectCut
  380. {
  381.     return inspectCut;
  382. }
  383. - orderFrontTools:sender
  384. {
  385.     [[tools window] orderFront:self];
  386.     return self;
  387. }
  388. /* Target/Action methods */
  389.  
  390. - help:sender
  391. {
  392.     int size;
  393.     char *data;
  394.     NXStream *stream;
  395.     id window, document;
  396.  
  397.     if (!(window = findDocument("/tmp/Help"))) {
  398.     data = getsectdata("HELP", "document", &size);
  399.     if (data) {
  400.         stream = NXOpenMemory(data, size, NX_READONLY);
  401.         if (stream) {
  402.         document = [DrawDocument newFromStream:stream];
  403.         [document setName:"Help" andDirectory:"/tmp"];
  404.         }
  405.         NXClose(stream);
  406.     }
  407.     } else {
  408.     [window makeKeyAndOrderFront:self];
  409.     }
  410.  
  411.     return self;
  412. }
  413.  
  414. - new:sender
  415. /*
  416.  * Called by pressing New in the Window menu.
  417.  */
  418. {
  419.     [DrawDocument new];
  420.     return self;
  421. }
  422.  
  423. - open:sender
  424. /*
  425.  * Called by pressing Open... in the Window menu.
  426.  */
  427. {
  428.     const char *directory;
  429.     const char *const *files;
  430.     const char **myfiles;
  431.     static const char *const drawType[2] = {"hdraw", NULL};
  432.     id openpanel = [[OpenPanel new] allowMultipleFiles:YES];
  433.     int        i, nfiles;
  434.  
  435.     directory = [[self currentDocument] directory];
  436.     if (directory && (*directory == '/')) [openpanel setDirectory:directory];
  437.     if ([openpanel runModalForTypes:drawType]) {
  438.     files = [openpanel filenames];
  439.     directory = [openpanel directory];
  440.     /*
  441.      * we made need to use the open panel again, so need to
  442.      * save results now is separate array
  443.      */
  444.     nfiles = 0;
  445.     while ( files && *files ) {
  446.         nfiles++;
  447.         files++;
  448.     }
  449.     files = [openpanel filenames];
  450.     NX_MALLOC( myfiles, const char *, nfiles );
  451.     for ( i = 0; i < nfiles; i++ ) {
  452.         myfiles[i] = NXCopyStringBuffer(*files);
  453.         files++;
  454.     }
  455.     for ( i = 0; i < nfiles; i++ ) {
  456.         haveOpenedDocument = openFile(directory, myfiles[i], YES)
  457.                           || haveOpenedDocument;
  458.         NX_FREE( myfiles[i] );
  459.     }
  460.     NX_FREE( myfiles );
  461.     }
  462.  
  463.     return self;
  464. }
  465. - openTuple:sender
  466. {
  467.     [inspectTuple openTuple:sender];
  468.     return self;
  469. }
  470. - openTupleAsText:sender
  471. {
  472.     [inspectTuple openTupleAsText:sender];
  473.     return self;
  474. }
  475. - openTuple:pasteBoard
  476.     userData:(const char *)args
  477.     error:(char **)errorMsg
  478. {
  479.     [[[DrawDocument new] view] openTuple:pasteBoard
  480.                                    userData:args
  481.                    error:errorMsg];
  482.  
  483.     return self;
  484. }
  485. - saveAll:sender
  486. /*
  487.  * Saves all the documents.
  488.  */
  489. {
  490.     int count;
  491.     id window, windowList;
  492.  
  493.     windowList = [NXApp windowList];
  494.     count = [windowList count];
  495.     while (count--) {
  496.     window = [windowList objectAt:count];
  497.     [documentInWindow(window) save:self];
  498.     }
  499.  
  500.     return nil;
  501. }
  502.  
  503. - terminate:sender
  504. /*
  505.  * Overridden to be sure all documents get an opportunity to be saved
  506.  * before exiting the program.
  507.  */
  508. {
  509.     int count, choice;
  510.     id window, windowList, document;
  511.  
  512.     windowList = [NXApp windowList];
  513.     count = [windowList count];
  514.     while (count--) {
  515.     window = [windowList objectAt:count];
  516.      document = [window delegate];
  517.     if ([document respondsTo:@selector(needsSaving)] && [document needsSaving]) {
  518.         choice = NXRunAlertPanel("Quit", "You have unsaved documents.", "Review Unsaved", "Quit Anyway", "Cancel");
  519.         if (choice == NX_ALERTOTHER)  {
  520.         return self;
  521.         } else if (choice == NX_ALERTDEFAULT) {
  522.         count = [windowList count];
  523.         while (count--) {
  524.             window = [windowList objectAt:count];
  525.             document = [window delegate];
  526.             if ([document respondsTo:@selector(windowWillClose:action:)]) {
  527.             if ([document windowWillClose:window action:"Quit"]) {
  528.                 [window close];
  529.             } else {
  530.                 return self;
  531.             }
  532.             }
  533.         }
  534.         }
  535.         break;
  536.     }
  537.     }
  538.     [NXApp terminate:sender];
  539.  
  540.     return nil;
  541. }
  542.     
  543. /*
  544.  * Application object delegate methods.
  545.  * Since we don't have an application delegate, messages that would
  546.  * normally be sent there are sent to the Application object itself instead.
  547.  */
  548.  
  549. - appDidInit:sender
  550. /*
  551.  * Makes the tool palette not ever become the key window.
  552.  * Check for files to open specified on the command line.
  553.  * Initialize the menus.
  554.  * If there are no open documents, then open a blank one.
  555.  */
  556. {
  557.     int i;
  558.     char buffer[MAXPATHLEN+1];
  559.     char *directory, *name, *ext;
  560.  
  561.     [[NXApp appListener ] setServicesDelegate:self];
  562.     [[tools window] removeFromEventMask:NX_KEYDOWNMASK|NX_KEYUPMASK];
  563.     [[tools window] orderFront:self];
  564.     [[tools window] setFloatingPanel:YES];
  565.  
  566.     if (NXArgc > 1) {
  567.     for (i = 1; i < NXArgc; i++) {
  568.         strcpy(buffer, NXArgv[i]);
  569.         ext = strrchr(buffer, '.');
  570.         if (!ext || strcmp(ext, ".hdraw")) strcat(buffer, ".hdraw");
  571.         name = strrchr(buffer, '/');
  572.         if (name) {
  573.         *name++ = '\0';
  574.         directory = buffer;
  575.         } else {
  576.         name = buffer;
  577.         directory = NULL;
  578.         }
  579.         haveOpenedDocument = openFile(directory, name, YES) || haveOpenedDocument;
  580.     }
  581.     }
  582.  
  583.     if (!haveOpenedDocument) [self new:self];
  584.  
  585.     if (NXGetDefaultValue([NXApp appName], "Quit")) {
  586.     [NXApp activateSelf:YES];
  587.     NXPing();
  588.     [self terminate:self];
  589.     }
  590.  
  591.     initMenu([NXApp mainMenu]);
  592.  
  593.     return self;
  594. }
  595.  
  596. - (int)appOpenFile:(const char *)path type:(const char *)type
  597. /*
  598.  * This method is performed whenever a user double-clicks on an icon
  599.  * in the Workspace Manager representing a Draw program document.
  600.  */
  601. {
  602.     id   currentDoc;
  603.     char *name;
  604.     char directory[MAXPATHLEN+1];
  605.  
  606.     if (type && !strcmp(type, "hdraw")) {
  607.     strcpy(directory, path);
  608.     name = strrchr(directory, '/');
  609.     if (name) {
  610.         *name++ = '\0';
  611.         if (openFile(directory, name, YES)) {
  612.         haveOpenedDocument = YES;
  613.         return YES;
  614.         }
  615.     }
  616.     }
  617.     
  618.     if ( type && !strcmp(type, "hippo") ) {
  619.         currentDoc = [self currentDocument];
  620.     if ( !currentDoc ) {
  621.         currentDoc = [DrawDocument new];
  622.     }
  623.     [ currentDoc openTupleFile:path ];
  624.     haveOpenedDocument = YES;
  625.     return YES;
  626.     }
  627.  
  628.     return NO;
  629. }
  630.  
  631. - (BOOL)appAcceptsAnotherFile:sender
  632. /*
  633.  * We accept any number of appOpenFile:type: messages.
  634.  */
  635. {
  636.     return YES;
  637. }
  638.  
  639. /* Listener/Speaker remote methods */
  640.  
  641. - (int)msgDirectory:(const char **)fullPath ok:(int *)flag
  642. {
  643.     *fullPath = [self currentDirectory];
  644.     if (*fullPath) {
  645.     *flag = YES;
  646.     } else {
  647.     *fullPath = "";
  648.     *flag = NO;
  649.     }
  650.     return 0;
  651. }
  652.  
  653. - (int)msgVersion:(const char **)aString ok:(int *)flag
  654. {
  655.     char buf[20];
  656.  
  657.     sprintf(buf, "2.0 (v%2d)", DrawVersion);
  658.     *aString = NXCopyStringBuffer(buf);
  659.     *flag = YES;
  660.  
  661.     return 0;
  662. }
  663.  
  664. - (int)msgFile:(const char **)fullPath ok:(int *)flag
  665. {
  666.     const char *file;
  667.  
  668.     file = [[self currentDocument] filename];
  669.     if (file) {
  670.     *fullPath = file;
  671.     *flag = YES;
  672.     } else {
  673.     *fullPath = "";
  674.     *flag = NO;
  675.     }
  676.  
  677.     return 0;
  678. }
  679.  
  680. - (BOOL)shouldRunPrintPanel:sender
  681. /*
  682.  * When NXApp prints, don't bring up the PrintPanel.
  683.  */
  684. {
  685.     return NO;
  686. }
  687.  
  688. - (int)msgPrint:(const char *)fullPath ok:(int *)flag
  689. {
  690.     BOOL close;
  691.     id document = nil;
  692.     char *directory, *name;
  693.     char path[MAXPATHLEN+1];
  694.     char buffer[MAXPATHLEN+1];
  695.  
  696.     InMsgPrint = YES;
  697.     strcpy(buffer, fullPath);
  698.     name = strrchr(buffer, '/');
  699.     if (name) {
  700.     *name++ = '\0';
  701.     directory = buffer;
  702.     } else {
  703.     name = buffer;
  704.     directory = NULL;
  705.     }
  706.     if (!chdir(directory) && getwd(path)) {
  707.     strcat(path, "/");
  708.     strcat(path, name);
  709.     document = [findDocument(path) delegate];
  710.     }
  711.     if (document) {
  712.     close = NO;
  713.     } else {
  714.     document = openFile(directory, name, NO);
  715.     if (document) haveOpenedDocument = YES;
  716.     close = YES;
  717.     }
  718.     if (document && ![[document view] isEmpty]) {
  719.     [NXApp setPrintInfo:[document printInfo]];
  720.     [[document view] printPSCode:self];
  721.     if (close) [[[document view] window] close];
  722.     *flag = YES;
  723.     } else {
  724.     *flag = NO;
  725.     }
  726.     InMsgPrint = NO;
  727.  
  728.     return 0;
  729. }
  730.  
  731. - (int)msgSelection:(const char **)bytes length:(int *)len
  732.     asType:(const char *)aType ok:(int *)flag
  733. {
  734.     int maxlen;
  735.     NXStream *stream;
  736.  
  737.     if (!strcmp(aType, DrawPboardType)) {
  738.     stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  739.     if ([[[self currentDocument] view] copySelectionToStream:stream]) {
  740.         NXGetMemoryBuffer(stream, bytes, len, &maxlen);
  741.         *flag = YES;
  742.     } else {
  743.         *flag = NO;
  744.     }
  745.     NXClose(stream);
  746.     } else if (!strcmp(aType, NXPostScriptPboardType)) {
  747.     stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  748.     if ([[[self currentDocument] view] copySelectionAsPS:stream]) {
  749.         NXGetMemoryBuffer(stream, bytes, len, &maxlen);
  750.         *flag = YES;
  751.     } else {
  752.         *flag = NO;
  753.     }
  754.     NXClose(stream);
  755.     } else if (!strcmp(aType, NXTIFFPboardType)) {
  756.     stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  757.     if ([[[self currentDocument] view] copySelectionAsTIFF:stream]) {
  758.         NXGetMemoryBuffer(stream, bytes, len, &maxlen);
  759.         *flag = YES;
  760.     } else {
  761.         *flag = NO;
  762.     }
  763.     NXClose(stream);
  764.     } else {
  765.     *flag = NO;
  766.     }
  767.  
  768.     if (!*flag) {
  769.     *bytes = "";
  770.     *len = 0;
  771.     }
  772.  
  773.     return 0;
  774. }
  775.  
  776. - (int)msgCopyAsType:(const char *)aType ok:(int *)flag
  777. {
  778.     if (!strcmp(aType, NXPostScriptPboardType) ||
  779.     !strcmp(aType, NXTIFFPboardType) ||
  780.     !strcmp(aType, DrawPboardType)) {
  781.     *flag = ([[[self currentDocument] view] copy:self] ? YES : NO);
  782.     } else {
  783.     *flag = NO;
  784.     }
  785.     return 0;
  786. }
  787.  
  788. - (int)msgCutAsType:(const char *)aType ok:(int *)flag
  789. {
  790.     if (!strcmp(aType, NXPostScriptPboardType) ||
  791.     !strcmp(aType, NXTIFFPboardType) ||
  792.     !strcmp(aType, DrawPboardType)) {
  793.     *flag = ([[[self currentDocument] view] cut:self] ? YES : NO);
  794.     } else {
  795.     *flag = NO;
  796.     }
  797.     return 0;
  798. }
  799.  
  800. - (int)msgPaste:(int *)flag;
  801. {
  802.     *flag = ([[[self currentDocument] view] paste:self] ? YES : NO);
  803.     return 0;
  804. }
  805.  
  806. - (int)msgQuit:(int *)flag
  807. {
  808.     *flag = ([self terminate:self] ? NO : YES);
  809.     return 0;
  810. }
  811.  
  812. /* Global cursor setting */
  813.  
  814. - cursor
  815. /*
  816.  * This is called by DrawDocument objects who want to set the cursor
  817.  * depending on what the currently selected tool is (as well as on whether
  818.  * the Control key has been pressed indicating that the select tool is
  819.  * temporarily set--see sendEvent:).
  820.  */
  821. {
  822.     id theCursor = nil;
  823.     if (!cursorPushed) theCursor = [[self currentGraphic] cursor];
  824.     return theCursor ? theCursor : NXArrow;
  825. }
  826.  
  827. - sendEvent:(NXEvent *)event
  828. /*
  829.  * We override this because we need to find out when the control key is down
  830.  * so we can set the arrow cursor so the user knows she is (temporarily) in
  831.  * select mode.
  832.  */
  833. {
  834.     if (event && event->type < NX_KITDEFINED) {    /* mouse or keyboard event */
  835.     if (event->flags & NX_CONTROLMASK) {
  836.         if (!cursorPushed && currentGraphic) {
  837.         cursorPushed = YES;
  838.         [[self currentDocument] resetCursor];
  839.         }
  840.     } else if (cursorPushed) {
  841.         cursorPushed = NO;
  842.         [[self currentDocument] resetCursor];
  843.     }
  844.     }
  845.  
  846.     //    return [super sendEvent:event];
  847.     return self;
  848. }
  849.  
  850. /* Automatic update methods */
  851.  
  852. - (BOOL)menuItemUpdate:menuCell
  853. /*
  854.  * Method called by all menu items which send their actions to the
  855.  * First Responder.  First, if the object which would respond were the
  856.  * action sent down the responder chain also responds to the message
  857.  * validateCommand:, then it is sent validateCommand: to determine
  858.  * whether that command is valid now, otherwise, if there is a responder
  859.  * to the message, then it is assumed that the item is valid.
  860.  * The method returns YES if the cell has changed its appearance (so that
  861.  * the caller (a Menu) knows to redraw it).
  862.  */
  863. {
  864.     SEL action;
  865.     id responder, target;
  866.     BOOL enable;
  867.  
  868.     target = [menuCell target];
  869.     enable = [menuCell isEnabled];
  870.  
  871.     if (!target) {
  872.     action = [menuCell action];
  873.     responder = [NXApp calcTargetForAction:action];
  874.     if ([responder respondsTo:@selector(validateCommand:)]) {
  875.         enable = [responder validateCommand:menuCell];
  876.     } else {
  877.         enable = responder ? YES : NO;
  878.     }
  879.     }
  880.  
  881.     if ([menuCell isEnabled] != enable) {
  882.     [menuCell setEnabled:enable];
  883.     return YES;
  884.     } else {
  885.     return NO;
  886.     }
  887. }
  888.  
  889. - (BOOL)validateCommand:menuCell
  890. {
  891.     SEL action = [menuCell action];
  892.  
  893.     if (action == @selector(saveAll:)) {
  894.     return findDocument(NULL) ? YES : NO;
  895.     }
  896.  
  897.     return YES;
  898. }
  899.  
  900. /*
  901.  * This is a very funky method and tricks of this sort are not generally
  902.  * recommended, but this hack is done so that new Graphic subclasses can
  903.  * be added to the program without changing even one line of code (except,
  904.  * of course, to implement the subclass itself).
  905.  *
  906.  * The objective-C method objc_getClass() is used to find the factory object
  907.  * corresponding to the name of the cell sending the setCurrentGraphic:
  908.  * message (this is set in InterfaceBuilder using the Miscellaneous panel
  909.  * in the Inspector window).
  910.  *
  911.  * Again, this is not recommended procedure, but it illustrates how
  912.  * objective-C can be used to make some funky runtime dependent decisions.
  913.  */
  914.  
  915. - setCurrentGraphic:sender
  916.  /*
  917.  * The sender's name is queried.  If that name corresponds to the name
  918.  * of a class, then that class is set as the currentGraphic.  If not,
  919.  * then the select tool is put into effect.
  920.  *
  921.  * Modified to handle Hippo 1D and 2D plots differently.   This is
  922.  * needed until their Graphic can draw rectangle while resizing, 
  923.  * then display plot when done. 
  924.  * also avoid error message associated with selecting
  925.  * selection tool -- Paul Kunz
  926.  */
  927. {
  928.     id cell;
  929.     const char *className;
  930.     int     tag;
  931.  
  932.     cell = [sender selectedCell];
  933.     if (cell) {
  934.         tag = [cell tag];
  935.       /*
  936.        * tag = -1 for selection tool
  937.        *     = -2 for Graphic object
  938.        *     = graphictype_t for Plot object
  939.        * tag is set with Interface Builder
  940.        */
  941.     if ( tag < 0 ) {
  942.         if ( tag != (-1) ) { /* avoid selection tool */
  943.         className = NXGetObjectName(cell);
  944.         if (className) {
  945.             currentGraphic = objc_getClass(className);
  946.         } else {
  947.             currentGraphic = nil;
  948.         }
  949.         } else {
  950.             currentGraphic = nil;
  951.         }
  952.         if (!currentGraphic) {
  953.             [tools selectCellWithTag:(-1)];
  954.         }
  955.         [[self currentDocument] resetCursor];
  956.     } else {
  957.         currentGraphic = [Plot class];
  958.         [self orderFrontTupleInsp:self];
  959.         [[[self currentDocument] view] addPlotOfType:tag];
  960.     }
  961.     } else {
  962.         currentGraphic = nil;
  963.         [tools selectCellWithTag:(-1)];
  964.     [[self currentDocument] resetCursor];
  965.     }
  966.  
  967.     return self;
  968. }
  969.  
  970. @end
  971.